import { EMessageC2S } from "../../common/EMessageC2S";
import { WrapRegistData } from "../../common/CommonTransData";
import { EClientType } from "../../common/EClientType";
import { TransPack } from "../../server/framework/common/TransPack";
import { EMessageS2C } from "../../common/EMessageS2C";
import { BaseInjectHandler } from "./BaseInjectHandler";
import { HandleQueryObjList } from "./handler/HandleQueryObjList";
import { HandleQueryObjDetail } from "./handler/HandleQueryObjDetail";
import { HandleSetObjActive } from "./handler/HandleSetObjActive";
import { HandleSetObjName } from "./handler/HandleSetObjName";
import { HandleLogToConsole } from "./handler/HandleLogToConsole";
import { HandleSetLocalPos } from "./handler/HandleSetLocalPos";
import { HandleSetLocalRot } from "./handler/HandleSetLocalRot";
import { HandleSetLocalScale } from "./handler/HandleSetLocalScale";
import { HandleQuerySceneImg } from "./handler/HandleQuerySceneImg";
import { HandleSetSceneCameraRot } from "./handler/HandleSetSceneCameraRot";
import { HandleSetSceneCameraPos } from "./handler/HandleSetSceneCameraPos";
import { HandleResetCameraPos } from "./handler/HandleResetCameraPos";
import { HandleSetEnableFog } from "./handler/HandleSetEnableFog";
import { HandleSetFogColor } from "./handler/HandleSetFogColor";
import { HandleSetFogRange } from "./handler/HandleSetFogRange";
import { HandleSetFogStart } from "./handler/HandleSetFogStart";
import { MsgPack } from "../../LTGame/3rd/msgpack/MsgPack";
import { PakoEx } from "LTGame/3rd/pako/PakoEx";

const TAG = '[DebugInject]';
const MAX_RECONNECT_COUNT = 1;

/**
 * 初始化之后独立运行,不影响主工程
 */
export class DebugInject {

    private static _instance: DebugInject;

    public static Init(remoteAddress: string, clientTag: string = 'layaGame') {
        if (this._instance != null) {
            console.error('DebugInject已经初始化');
            return;
        }
        this._instance = new DebugInject();
        this._instance._Init(remoteAddress, clientTag);
    }

    private _ws: WebSocket;

    private _isInited: boolean = false;

    private _state: EClientState;

    private _cacheAddress: string;

    private _nextConnectTick: number;
    private _reconnectCount: number;
    private _lastHeartTick: number;

    private _handleMap: Map<number, BaseInjectHandler>;

    private _cacheTag: string;

    private _Init(remoteAddress: string, clientTag: string = 'layaGame') {
        this._cacheTag = clientTag;
        this._state = EClientState.NotConnectServer;
        this._cacheAddress = remoteAddress;
        this._reconnectCount = 0;
        this._handleMap = new Map<number, BaseInjectHandler>();
        this._RegistHandle(new HandleQueryObjList(this));
        this._RegistHandle(new HandleQueryObjDetail(this));
        this._RegistHandle(new HandleSetObjActive(this));
        this._RegistHandle(new HandleSetObjName(this));
        this._RegistHandle(new HandleLogToConsole(this));
        this._RegistHandle(new HandleSetLocalPos(this));
        this._RegistHandle(new HandleSetLocalRot(this));
        this._RegistHandle(new HandleSetLocalScale(this));
        this._RegistHandle(new HandleQuerySceneImg(this));
        this._RegistHandle(new HandleSetSceneCameraRot(this));
        this._RegistHandle(new HandleSetSceneCameraPos(this));
        this._RegistHandle(new HandleResetCameraPos(this));
        this._RegistHandle(new HandleSetEnableFog(this));
        this._RegistHandle(new HandleSetFogColor(this));
        this._RegistHandle(new HandleSetFogRange(this));
        this._RegistHandle(new HandleSetFogStart(this));
        this._InitLoop();
    }

    public SendTransOtherData(msgId: number, data: any = null) {
        if (data == null) {
            this.SendData(EMessageC2S.TransToOther, { type: msgId });
        } else {
            this.SendData(EMessageC2S.TransToOther, { type: msgId, data: data });
        }
    }

    public SendData(msgId: number, data: any = null) {
        let wrapData = new TransPack();
        wrapData.type = msgId;
        if (data != null) {
            wrapData.data = data;
        }
        // let json = JSON.stringify(wrapData);
        let sendPack = MsgPack.Encode(wrapData);
        let compressedData = PakoEx.Compress(sendPack);
        this._ws.send(compressedData);
    }

    public GetHandleById(msgId: number): BaseInjectHandler {
        return this._handleMap.get(msgId);
    }

    private _RegistHandle(handle: BaseInjectHandler) {
        if (this._handleMap.has(handle.msgId)) {
            console.error(TAG, "消息id已注册");
            return
        }
        this._handleMap.set(handle.msgId, handle);
    }

    private _InitLoop() {
        if (this._isInited) return;
        this._isInited = true;
        Laya.timer.frameLoop(1, this, this._LogicUpdate);
    }

    private _LogicUpdate() {
        switch (this._state) {
            case EClientState.NotConnectServer:
                this._Connect(this._cacheAddress);
                break;
            case EClientState.ConnectedServer:
                this._HandleHeart();
                break;
            case EClientState.ConnectedDebugger:
                this._HandleHeart();
                break;
            case EClientState.DisConnected:
                let nowTick = Date.now();
                if (nowTick > this._nextConnectTick) {
                    this._reconnectCount++;
                    this._Connect(this._cacheAddress);
                }
                break;
        }
    }

    private _HandleHeart() {
        if (this._state != EClientState.ConnectedServer
            && this._state != EClientState.ConnectedDebugger) {
            console.error(TAG, "状态错误");
            return;
        }
        let nowTick = Date.now();
        let passTick = nowTick - this._lastHeartTick;
        if (passTick > 10 * 1000) {
            this.SendData(EMessageC2S.Heart);
            this._lastHeartTick = nowTick;
        }
    }

    private async _HandleRecvMsg(msg: MessageEvent) {
        let blobData = msg.data as Blob;
        let ab = await blobData.arrayBuffer();
        let uint8Array = new Uint8Array(ab);
        let decompressData = PakoEx.UnCompress(uint8Array);
        let transPack = MsgPack.Decode(decompressData) as TransPack;
        switch (transPack.type) {
            case EMessageS2C.TransToOther:
                this._HandlePayLoad(transPack.data);
                break;
            default:
                console.error(TAG, "未定义的主消息id:" + transPack.type);
                break;
        }
    }

    private _HandlePayLoad(data: TransPack) {
        let getHandle = this._handleMap.get(data.type);
        if (getHandle == null) {
            console.error(TAG, "未处理的消息id:" + data.type);
            return;
        }
        getHandle.HandleMsg(data);
    }

    private _Connect(remoteAddress: string) {
        if (this._reconnectCount > MAX_RECONNECT_COUNT) {
            console.error("已达到最大重连次数", MAX_RECONNECT_COUNT);
            this._state = EClientState.Error;
            return;
        }
        if (this._state != EClientState.NotConnectServer
            && this._state != EClientState.DisConnected) {
            console.error(TAG, "状态错误");
            return;
        }
        if (WebSocket == null) {
            console.error(TAG, "当前环境不支持websocket");
            this._state = EClientState.Error;
            return;
        }
        this._state = EClientState.ConnectingServer;
        this._ws = new WebSocket(remoteAddress, 'echo-protocol');

        this._ws.onopen = (socket) => {
            console.log(TAG, "连接成功");
            this._reconnectCount = 0;
            this._state = EClientState.ConnectedServer;
            this._lastHeartTick = Date.now();
            let fakeData = new WrapRegistData();
            fakeData.registType = EClientType.Client;
            fakeData.clientName = this._cacheTag;
            this.SendData(EMessageC2S.Regist, fakeData);
        }
        this._ws.onmessage = (msg: MessageEvent) => {
            this._HandleRecvMsg(msg);
        }
        this._ws.onclose = (msg) => {
            console.log(TAG, "链接已断开", msg);
            this._state = EClientState.DisConnected;
            this._nextConnectTick = Date.now() + 1000;
        }
    }
}

enum EClientState {

    NotConnectServer = 0,
    ConnectingServer,
    ConnectedServer,
    ConnectedDebugger,
    DisConnected,
    Error,

}

window["ltg"] = {
    DebugInject: DebugInject,
};
